/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * GUI test
 * 
 * @author Benjamin Adcock
 */

package hms.UserAccountsModule;

import hms.generic.AbstractGlobalView;
import hms.permissions.Permission;
import hms.properties.Properties;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class UserAccountView extends AbstractGlobalView implements ListSelectionListener {

	///////////////////////////
	//// Global GUI Fields ////
	///////////////////////////
	
	JLabel username;
	JLabel status;
	JList allUserList;
	JCheckBox[] permissionsBoxes;
	JButton editButton;
	JButton removeButton;
	JButton commitButton;
	DefaultListModel listModel;

	// Database Model Object
	UserAccountControl allAccounts;

	/////////////////////
	//// Constructor ////
	/////////////////////
	
	public UserAccountView() {

		// Load user accounts
		// UserAccountControl implements ListModel
		allAccounts = new UserAccountControl();

		//////////////////////////////////////////////////////
		//// Create GUI elements and listener assignments ////
		//////////////////////////////////////////////////////
		
		// Username
		username = new JLabel("<nothing selected>");
		username.setFont(new Font("Sans Serif", Font.BOLD, 18));
		
		// Status
		status = new JLabel("---");
		status.setFont(new Font("Sans Serif", Font.BOLD, 14));
		
		// Checkboxes
		JPanel permissions = new JPanel();
		permissions.setBorder(BorderFactory.createTitledBorder("Permissions"));
		// Add some check boxes
		addCheckboxesToPanel(permissions);
		
		// List
		// List and scroll pane for the list
		listModel = new DefaultListModel();
		syncListModel();
		allUserList = new JList(listModel);
		allUserList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		//allUserList.setCellRenderer( new UserAccountLabel() ); 
		allUserList.addListSelectionListener( this );
		allUserList.setLayoutOrientation(JList.VERTICAL);
		JScrollPane listScrollPane = new JScrollPane(allUserList);

		// Buttons
		JButton addButton = new JButton("Add...");
		addButton.setActionCommand("Add");
		addButton.addActionListener(new AddListener());
		
		editButton = new JButton("Edit Info...");
		editButton.setEnabled(false);
		editButton.setActionCommand("Edit");
		editButton.addActionListener(new EditListener());
		
		removeButton = new JButton("Remove");
		removeButton.setEnabled(false);
		removeButton.setActionCommand("Remove");
		removeButton.addActionListener(new RemoveListener());
		
		commitButton = new JButton("Commit to DB");
		commitButton.setEnabled(false);
		commitButton.setActionCommand("Commit");
		commitButton.addActionListener(new CommitListener());
		
		/////////////////////////////////
		//// Create and Layout Panel ////
		/////////////////////////////////
		
		JPanel panel = new JPanel();
		panel.setLayout(new GridBagLayout()); // Set layout to GridBagLayout style
		GridBagConstraints c;

		
		// Username and status
		c = new GridBagConstraints();
		// These are grid coordinates.  Starts in upper-left corner with 0,0.
		c.gridx = 0; c.gridy = 0;
		// Weightx - relative width of this column (0) compared to other columns
		c.weightx = 0.65;
		// Stretch label horizontally to fill space
		c.fill = GridBagConstraints.HORIZONTAL;
		// Anchor text to bottom-left of available space in grid
		// I want this text to butt-up with the next label
		c.anchor = GridBagConstraints.LAST_LINE_START;
		// Padding - 10px all around works well, but again, I want this label
		// to butt up with the next one.
		// new Insets (top, left, bottom, right);
		c.insets = new Insets(10, 10, 0, 10);
		// Add username
		panel.add(username, c);
		// Anchor to top-left corner
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		// Far left column, second row
		c.gridy = 1;
		c.insets = new Insets(0, 10, 10, 10);
		// Add status
		panel.add(status, c);
		
		// Checkboxes
		c = new GridBagConstraints();
		c.weightx = 0.65;
		// Do NOT stretch the panel any bigger than it already is
		c.fill = GridBagConstraints.NONE;
		// Anchor to left side of window, centered vertically
		c.anchor = GridBagConstraints.LINE_START;
		// Left column, 3rd row
		c.gridx = 0;
		c.gridy = 2;
		// More padding
		c.insets = new Insets(15, 30, 15, 30);
		panel.add(permissions, c);
		
		
		c = new GridBagConstraints();
		// Use 35% of total width
		c.weightx = 0.35;
		// Use all the height that is available!
		c.weighty = 1;
		// Stretch to given space
		c.fill = GridBagConstraints.BOTH;
		// Third column (second was originally padding), 1st row
		c.gridx = 2;
		c.gridy = 0;
		// Take up three rows (Labels and permissions box) and 2 columns (buttons below)
		c.gridheight = 3;
		c.gridwidth = 2;
		c.insets = new Insets(10, 10, 10, 10);
		panel.add(listScrollPane, c);
		
		// Four buttons with similar constraints except for grid positions.
		c = new GridBagConstraints();
		// Stretch horizontally
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = .175; // Half of 35%
		c.weighty = 0;
		c.gridx = 2;
		c.gridy = 3;
		// Internally pad as well as externally pad
		c.ipadx = 10;
		c.ipady = 10;
		c.insets = new Insets(10, 10, 10, 10);
		panel.add(addButton, c);

		c.gridx = 3;
		c.gridy = 3;
		panel.add(editButton, c);

		c.gridx = 2;
		c.gridy = 4;
		panel.add(removeButton, c);

		c.gridx = 3;
		c.gridy = 4;
		panel.add(commitButton, c);

		///////////////////////
		//// Display Panel ////
		///////////////////////
		
		updateGUI();
		super.update(panel);
		super.display();
	}

	/////////////////////////////////////////
	//// Convenience Classes and Methods ////
	/////////////////////////////////////////
	
	private class Credentials {
		private String username;
		private String password;
		private boolean valid;
		
		public Credentials(String username, String password, boolean validity) {
			this.username = username;
			this.password = password;
			this.valid = validity;
		}
		
		public UserAccount createUser() {
			if(valid) {
				return new UserAccount(username,password);
			} else {
				return null;
			}
		}
		
		public boolean isValid() {
			if(!valid) { return false; }
			if(password.length() < 8) { return false; }
			if(username.length() == 0) { return false; }
			return true;
		}
		
		public boolean isUnique() {
			UserAccountControl uac = new UserAccountControl();
			for(UserAccount ua : uac) {
				if(ua.getUserName().equalsIgnoreCase(username)) { return false; }
			}
			return true;
		}
		
		public boolean updateUser(UserAccount user) {
			if(valid) {
				user.setUsername(username);
				user.setPassword(password);
			}
			return valid;
		}
	}
	
	// TODO [ben] comment
	public void addCheckboxesToPanel(JPanel panel) {
		// Allocate room for check boxes
		permissionsBoxes = new JCheckBox[Permission.values().length];
		
		// Define layout
        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));

		for (Permission p : Permission.values()) {
			int i = p.ordinal();
			// Create
			permissionsBoxes[i] = new JCheckBox(p.toString());
			// Listener
			permissionsBoxes[i].setActionCommand("Permission "+p.toString()+" Change");
			permissionsBoxes[i].addActionListener( new PermissionListener(permissionsBoxes[i], p) );
			if(p == Permission.FULL_ACCESS && !Properties.currentUser.isSpecificPermissionGranted(Permission.FULL_ACCESS)) {
				permissionsBoxes[i].setVisible(false);
			}
			// Layout
			if(i!=0) {
				panel.add(Box.createVerticalStrut(5));
			}
			panel.add(permissionsBoxes[i]);
		}
	}
	
	
	/**
	 * Returns UserAcocunt currently selected by allUserList.
	 * 
	 * @return UserAccount that is selected
	 */
	private UserAccount getCurrentlySelectedUser() {
		int index = allUserList.getSelectedIndex();
		return allAccounts.getFromDisplayIndex(index);
	}

	
	///////////////////////////////////////
	//// List Selection Handler Method ////
	///////////////////////////////////////
	
	@Override
	public void valueChanged(ListSelectionEvent arg0) {
		if (arg0.getValueIsAdjusting() == false) {
			updateGUI();
		}
	}
	
	private void updateGUI() {
		int index = allUserList.getSelectedIndex();
		if (index == -1) {
			username.setText("<nothing selected>");
			status.setText("---");
			for (Permission p : Permission.values()) {
				permissionsBoxes[p.ordinal()].setEnabled(false);
			}
			removeButton.setEnabled(false);
			editButton.setEnabled(false);
		} else {
			UserAccount account = (UserAccount) allAccounts.get(index);
			username.setText(account.toString());
			status.setText(account.getStatusString());
			for (Permission p : Permission.values()) {
				permissionsBoxes[p.ordinal()].setEnabled(true);
				permissionsBoxes[p.ordinal()].setSelected(account
						.isSpecificPermissionGranted(p));
			}
			removeButton.setEnabled(true);
			editButton.setEnabled(true);
		}
	}
	
	////////////////////////////////
	//// Data Integrity Methods ////
	////////////////////////////////
	
	@Override
	public void informationWillBeLost() {
		super.informationWillBeLost();
		commitButton.setEnabled(true);
	}
	
	private void syncListModel() {
		listModel.clear();
		listModel.ensureCapacity(allAccounts.size());
		for(UserAccount ua : allAccounts) {
			if(!ua.isMarkedForDeletion())
			listModel.addElement(ua);
		}
	}
	
	//////////////////////////////
	//// Listener Definitions ////
	//////////////////////////////
	
	class AddListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			Credentials creds = addNewUserDialog();
			if (creds.isValid() && creds.isUnique()) {
				// Get index of new user
				int index = allAccounts.size();
				// Add user and sync list
				allAccounts.add(creds.createUser());
				syncListModel();
				informationWillBeLost();

				allUserList.setSelectedIndex(index);
				allUserList.ensureIndexIsVisible(index);
			} else {
				JOptionPane
						.showMessageDialog(
								null,
								"User cannot be created!\nPassword must be 8 characters or more.\nUsername must be unique.",
								"Bad Credentials",
								0);
			}

		}
	}
	
	class EditListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			int index = allUserList.getSelectedIndex();
			UserAccount target = allAccounts.getFromDisplayIndex(index);
			Credentials creds = editUserDialog(target.getUserName());
			if(creds.isValid()) {
				creds.updateUser(target);
				syncListModel();
				informationWillBeLost();
			} else {
				JOptionPane
				.showMessageDialog(
						null,
						"User cannot be edited!\nPassword must be 8 characters or more.\nUsername must be unique.",
						"Bad Credentials",
						0);
			}
		}
	}
	
	class RemoveListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			int index = allUserList.getSelectedIndex();
			UserAccount target = getCurrentlySelectedUser();
			target.markToBeDeleted();
			
			// Update display
			syncListModel();
			// Mark data as tentative
			informationWillBeLost();
			
			// Select another user
			if(index<listModel.size()) {
				allUserList.setSelectedIndex(index);
				allUserList.ensureIndexIsVisible(index);
			} else if(!listModel.isEmpty()) {
				allUserList.setSelectedIndex(listModel.size()-1);
				allUserList.ensureIndexIsVisible(listModel.size()-1);
			}
		}
	}
	
	class CommitListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			allAccounts.commitToDatabase();
			informationHasBeenSaved();
			syncListModel();
			updateGUI();
			commitButton.setEnabled(false);
			JOptionPane.showConfirmDialog(null, 
					"Changes have been saved to the database.",
					"Saved!",
					JOptionPane.DEFAULT_OPTION,
					JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	class PermissionListener implements ActionListener {
		
		private JCheckBox box;
		private Permission permission;
		
		public PermissionListener(JCheckBox box, Permission p) {
			this.box = box;
			this.permission = p;
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			UserAccount target = getCurrentlySelectedUser();
			if(box.isSelected()) {
				target.grantPermission(permission);

				System.out.println(permission + " granted");
			} else {
				target.denyPermission(permission);

				System.out.println(permission + " denied");
			}
			informationWillBeLost();
		}
	}
	
	//////////////////////
	//// Pop-up Menus ////
	//////////////////////
	
	private Credentials addNewUserDialog(){
		return askForCredentials("Add new user", "");
	}
	
	private Credentials editUserDialog(String username){
		return askForCredentials("Edit user "+username, username);
	}
	
	private Credentials askForCredentials(String title, String username){
		
		JTextField usernameField = new JTextField();
		usernameField.setText(username);
		JPasswordField passwordField = new JPasswordField();
		
		JLabel usernameLabel = new JLabel("Username:");
		JLabel passwordLabel = new JLabel("Password:");

		Object[] componentsArray = { usernameLabel, 
                usernameField,
                passwordLabel,
                passwordField};
		
		Object[] options = {"OK", "Cancel"};
		
		int response = JOptionPane.showOptionDialog(null, 
				componentsArray, 
				title,
				JOptionPane.OK_CANCEL_OPTION,
                JOptionPane.PLAIN_MESSAGE,
                null,
                options,
                componentsArray[1]);
		
		return new Credentials(usernameField.getText().trim(), new String(passwordField.getPassword()), response == JOptionPane.YES_OPTION);
		
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		
		
	}
}
